index.js ➔ validateFloatType   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
/*
2
 * Copyright (c) 2017-2019 Rafael da Silva Rocha.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining
5
 * a copy of this software and associated documentation files (the
6
 * "Software"), to deal in the Software without restriction, including
7
 * without limitation the rights to use, copy, modify, merge, publish,
8
 * distribute, sublicense, and/or sell copies of the Software, and to
9
 * permit persons to whom the Software is furnished to do so, subject to
10
 * the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
25
/**
26
 * @fileoverview JavaScript binary parser for any browser or environment.
27
 * @see https://github.com/rochars/byte-data
28
 */
29
30
/** @module byte-data */
31
32
import { endianness } from './lib/endianness';
33
import { pack as packUTF8, unpack as unpackUTF8 } from './lib/utf8-parser';
34
import { IntParser } from './lib/int-parser';
35
import { FloatParser } from './lib/float-parser';
36
37
/**
38
 * Read a string of UTF-8 characters from a byte buffer.
39
 * @param {!(Uint8Array|Array<number>)} buffer A byte buffer.
40
 * @param {number} [index=0] The buffer index to start reading.
41
 * @param {number} [end=buffer.length] The index to stop reading, non inclusive.
42
 * @return {string}
43
 */
44
export function unpackString(buffer, index=0, end=buffer.length) {
45
  return unpackUTF8(buffer, index, end);
46
}
47
48
/**
49
 * Write a string of UTF-8 characters as a byte buffer.
50
 * @param {string} str The string to pack.
51
 * @return {!Array<number>} The UTF-8 string bytes.
52
 * @throws {TypeError} If 'str' is not a string.
53
 */
54
export function packString(str) {
55
  /** @type {!Array<number>} */
56
  let buffer = [];
57
  packUTF8(str, buffer);
58
  return buffer;
59
}
60
61
/**
62
 * Write a string of UTF-8 characters to a byte buffer.
63
 * @param {string} str The string to pack.
64
 * @param {!(Uint8Array|Array<number>)} buffer The output buffer.
65
 * @param {number} [index=0] The buffer index to start writing.
66
 * @return {number} The next index to write in the buffer.
67
 * @throws {TypeError} If 'str' is not a string.
68
 */
69
export function packStringTo(str, buffer, index=0) {
70
  return packUTF8(str, buffer, index);
71
}
72
73
// Numbers
74
/**
75
 * Pack a array of numbers to a byte buffer.
76
 * All other packing functions are interfaces to this function.
77
 * @param {!(Array<number>|TypedArray)} values The values to pack.
78
 * @param {!{bits:number,
79
 *   fp: (boolean|undefined),
80
 *   signed: (boolean|undefined),
81
 *   be: (boolean|undefined)}} theType The type definition.
82
 * @param {!(Uint8Array|Array<number>)} buffer The buffer to write on.
83
 * @param {number} [index=0] The buffer index to start writing.
84
 * @param {boolean} [clamp=false] True to clamp ints on overflow.
85
 * @return {number} The next index to write.
86
 * @throws {Error} If the type definition is not valid.
87
 * @throws {RangeError} On overflow if clamp is set to false.
88
 * @throws {TypeError} If 'values' is not a array of numbers.
89
 * @throws {TypeError} If 'values' is not a array of ints and type is int.
90
 */
91
export function packArrayTo(values, theType, buffer, index=0, clamp=false) {
92
  theType = theType || {};
93
  /** @type {!Object} */
94
  let packer = getParser_(theType.bits, theType.fp, theType.signed, clamp);
95
  /** @type {number} */
96
  let offset = Math.ceil(theType.bits / 8);
97
  /** @type {number} */
98
  let i = 0;
99
  /** @type {number} */
100
  let start = index;
101
  try {
102
    for (let valuesLen = values.length; i < valuesLen; i++) {
103
      index = packer.pack(buffer, values[i], index);
104
    }
105
    if (theType.be) {
106
      endianness(buffer, offset, start, index);
107
    }
108
  } catch (e) {
109
    throwValueError_(e, values[i], i);
110
  }
111
  return index;
112
}
113
114
/**
115
 * Unpack a array of numbers from a byte buffer to a array or a typed array.
116
 * All other unpacking functions are interfaces to this function.
117
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer.
118
 * @param {!{bits:number,
119
 *   fp: (boolean|undefined),
120
 *   signed: (boolean|undefined),
121
 *   be: (boolean|undefined)}} theType The type definition.
122
 * @param {!(TypedArray|Array<number>)} output The output array or typed array.
123
 * @param {number} [start=0] The buffer index to start reading.
124
 * @param {number} [end=buffer.length] The buffer index to stop reading.
125
 * @param {boolean} [safe=false] If set to false, extra bytes in the end of
126
 *   the input array are ignored and input buffers with insufficient bytes will
127
 *   write nothing to the output array. If safe is set to true the function
128
 *   will throw a 'Bad buffer length' error on the aforementioned cases.
129
 * @throws {Error} If the type definition is not valid.
130
 * @throws {Error} On bad input buffer length if on safe mode.
131
 */
132
export function unpackArrayTo(
133
    buffer, theType, output, start=0, end=buffer.length, safe=false) {
134
  theType = theType || {};
135
  /** @type {!Object} */
136
  let parser = getParser_(theType.bits, theType.fp, theType.signed, false);
137
  // getUnpackLen_ will adjust the end index according to the size
138
  // of the input buffer and the byte offset or throw a error on bad
139
  // end index if safe=true
140
  end = getUnpackLen_(buffer, start, end, parser.offset, safe);
141
  if (theType.be) {
142
    /** @type {!(Uint8Array|Array<number>)} */
143
    let readBuffer = copyBuffer_(buffer);
144
    if (theType.be) {
145
      endianness(readBuffer, parser.offset, start, end);
146
    }
147
    unpack_(readBuffer, output, start, end, parser);
148
  } else {
149
    unpack_(buffer, output, start, end, parser);
150
  }
151
}
152
153
/**
154
 * Pack a number to a byte buffer.
155
 * @param {number} value The value.
156
 * @param {!{bits:number,
157
 *   fp: (boolean|undefined),
158
 *   signed: (boolean|undefined),
159
 *   be: (boolean|undefined)}} theType The type definition.
160
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer to write on.
161
 * @param {number} [index=0] The buffer index to write.
162
 * @param {boolean} [clamp=false] True to clamp ints on overflow.
163
 * @return {number} The next index to write.
164
 * @throws {Error} If the type definition is not valid.
165
 * @throws {RangeError} On overflow if clamp is set to false.
166
 * @throws {TypeError} If 'value' is not a number.
167
 * @throws {TypeError} If 'value' is not a int and type is int.
168
 */
169
export function packTo(value, theType, buffer, index=0, clamp=false) {
170
  return packArrayTo([value], theType, buffer, index, clamp);
171
}
172
173
/**
174
 * Pack a number as a array of bytes.
175
 * @param {number} value The number to pack.
176
 * @param {!{bits:number,
177
 *   fp: (boolean|undefined),
178
 *   signed: (boolean|undefined),
179
 *   be: (boolean|undefined)}} theType The type definition.
180
 * @param {boolean} [clamp=false] True to clamp ints on overflow.
181
 * @return {!Array<number>} The packed value.
182
 * @throws {Error} If the type definition is not valid.
183
 * @throws {RangeError} On overflow if clamp is set to false.
184
 * @throws {TypeError} If 'value' is not a number.
185
 * @throws {TypeError} If 'value' is not a int and type is int.
186
 */
187
export function pack(value, theType, clamp=false) {
188
  /** @type {!Array<number>} */
189
  let output = [];
190
  packTo(value, theType, output, 0, clamp);
191
  return output;
192
}
193
194
/**
195
 * Unpack a number from a byte buffer.
196
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer.
197
 * @param {!{bits:number,
198
 *   fp: (boolean|undefined),
199
 *   signed: (boolean|undefined),
200
 *   be: (boolean|undefined)}} theType The type definition.
201
 * @param {number} [index=0] The buffer index to read.
202
 * @param {boolean} [safe=false] If set to false, extra bytes in the end of
203
 *   the input array are ignored and input buffers with insufficient bytes will
204
 *   write nothing to the output array. If safe is set to true the function
205
 *   will throw a 'Bad buffer length' error on the aforementioned cases.
206
 * @return {number}
207
 * @throws {Error} If the type definition is not valid.
208
 * @throws {Error} On bad input buffer length if on safe mode.
209
 */
210
export function unpack(buffer, theType, index=0, safe=false) {
211
  let output = [];
212
  unpackArrayTo(buffer, theType, output,
213
    index, index + Math.ceil(theType.bits / 8), safe);
214
  return output[0];
215
}
216
217
/**
218
 * Pack a array of numbers as a array of bytes.
219
 * @param {!(Array<number>|TypedArray)} values The values to pack.
220
 * @param {!{bits:number,
221
 *   fp: (boolean|undefined),
222
 *   signed: (boolean|undefined),
223
 *   be: (boolean|undefined)}} theType The type definition.
224
 * @param {boolean} [clamp=false] True to clamp ints on overflow.
225
 * @return {!Array<number>} The packed values.
226
 * @throws {Error} If the type definition is not valid.
227
 * @throws {RangeError} On overflow if clamp is set to false.
228
 * @throws {TypeError} If 'values' is not a array of numbers.
229
 * @throws {TypeError} If 'values' is not a array of ints and type is int.
230
 */
231
export function packArray(values, theType, clamp=false) {
232
  /** @type {!Array<number>} */
233
  let output = [];
234
  packArrayTo(values, theType, output, 0, clamp);
235
  return output;
236
}
237
238
/**
239
 * Unpack a array of numbers from a byte buffer.
240
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer.
241
 * @param {!{bits:number,
242
 *   fp: (boolean|undefined),
243
 *   signed: (boolean|undefined),
244
 *   be: (boolean|undefined)}} theType The type definition.
245
 * @param {number} [start=0] The buffer index to start reading.
246
 * @param {number} [end=buffer.length] The buffer index to stop reading.
247
 * @param {boolean} [safe=false] If set to false, extra bytes in the end of
248
 *   the input array are ignored and input buffers with insufficient bytes will
249
 *   write nothing to the output array. If safe is set to true the function
250
 *   will throw a 'Bad buffer length' error on the aforementioned cases.
251
 * @return {!Array<number>}
252
 * @throws {Error} If the type definition is not valid.
253
 * @throws {Error} On bad input buffer length if on safe mode.
254
 */
255
export function unpackArray(
256
    buffer, theType, start=0, end=buffer.length, safe=false) {
257
  /** @type {!Array<number>} */
258
  let output = [];
259
  unpackArrayTo(buffer, theType, output, start, end, safe);
260
  return output;
261
}
262
263
/**
264
 * Unpack a array of numbers from a byte buffer to a array or a typed array.
265
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer.
266
 * @param {!(TypedArray|Array<number>)} output The output array or typed array.
267
 * @param {number} start The buffer index to start reading.
268
 * @param {number} end The buffer index to stop reading.
269
 * @param {!Object} parser The parser.
270
 * @throws {Error} If the type definition is not valid.
271
 * @private
272
 */
273
function unpack_(buffer, output, start, end, parser) {
274
  /** @type {number} */
275
  let offset = parser.offset;
276
  for (let index = 0, j = start; j < end; j += offset, index++) {
277
    output[index] = parser.unpack(buffer, j);
278
  }
279
}
280
281
/**
282
 * Copy a byte buffer as a Array or Uint8Array.
283
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer.
284
 * @return {!(Uint8Array|Array<number>)}
285
 * @private
286
 */
287
function copyBuffer_(buffer) {
288
  if (buffer.constructor === Array) {
289
    return buffer.slice();
290
  }
291
  return new Uint8Array(buffer);
292
}
293
294
/**
295
 * Throw a error with information about the problem.
296
 * @param {!Object} err The Error object that is being raised.
297
 * @param {*} value The value that caused the error.
298
 * @param {number} index The index of the value that caused the error.
299
 * @throws {RangeError|TypeError|Error} A Error with a message.
300
 * @private
301
 */
302
function throwValueError_(err, value, index) {
303
  err.message = err.constructor.name +
304
    ' at index ' + index + ': ' + value;
305
  throw err;
306
}
307
308
/**
309
 * Adjust the end index according to the input buffer length and the
310
 * type offset.
311
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer.
312
 * @param {number} start The buffer index to start reading.
313
 * @param {number} end The buffer index to stop reading.
314
 * @param {number} offset The number of bytes used by the type.
315
 * @param {boolean} safe True for size-safe buffer reading.
316
 * @throws {Error} On bad buffer length, if safe.
317
 * @private
318
 */
319
function getUnpackLen_(buffer, start, end, offset, safe) {
320
  /** @type {number} */
321
  let extra = (end - start) % offset;
322
  if (safe && (extra || buffer.length < offset)) {
323
    throw new Error('Bad buffer length');
324
  }
325
  return end - extra;
326
}
327
328
/**
329
 * Return a parser for int, uint or fp numbers.
330
 * @param {number} bits The number of bits.
331
 * @param {boolean|undefined} fp True for fp numbers, false otherwise.
332
 * @param {boolean|undefined} signed True for signed ints, false otherwise.
333
 * @param {boolean|undefined} clamp True to clamp integers, false otherwise.
334
 * @return {!Object}
335
 * @private
336
 */
337
function getParser_(bits, fp, signed, clamp) {
338
  if (fp) {
339
    validateFloatType(bits);
340
  } else {
341
    validateIntType(bits);
342
  }
343
  if (fp && bits === 16) {
344
    return new FloatParser(5, 11);
345
  } else if (fp && bits == 32) {
346
    return new FloatParser(8, 23);
347
  } else if(fp && bits == 64) {
348
    return new FloatParser(11, 52);
349
  }
350
  return new IntParser(bits, signed, clamp);
351
}
352
353
/**
354
 * The type definition error message.
355
 * @type {string}
356
 * @private
357
 */
358
const TYPE_ERR = 'Unsupported type';
359
360
/**
361
 * Validate the type definition of floating-point numbers.
362
 * @param {number} bits The number of bits.
363
 * @throws {Error} If the type definition is not valid.
364
 * @private
365
 */
366
function validateFloatType(bits) {
367
  if (!bits || bits !== 16 && bits !== 32 && bits !== 64) {
368
    throw new Error(TYPE_ERR + ': float, bits: ' + bits);
369
  }
370
}
371
372
/**
373
 * Validate the type definition of integers.
374
 * @param {number} bits The number of bits.
375
 * @throws {Error} If the type definition is not valid.
376
 * @private
377
 */
378
function validateIntType(bits) {
379
  if (!bits || bits < 1 || bits > 53) {
380
    throw new Error(TYPE_ERR + ': int, bits: ' + bits);
381
  }
382
}
383